// System Unit
// Modular Support - EXE

(*************)
(* Interface *)
(*************)
{$IFDEF Include_Modules_Header}

 Var
  ExitCode: Cardinal = 0;              { Program return code }
  _RTL_SafeShutdown: Boolean = False;  { Set of the program is shutdown in a safe way }

 Procedure _Halt(Code: Cardinal);
 Procedure _Halt0;

 { TLS Support }
 Procedure _RTL_TLSProcs(Const TLSStartThread, TLSEndThread, TLSExitProcess: TProc);

 // EXE specific preparation (for internal use)
 Procedure _RTL_PrepareExe(Const LibModule: PLibModule);

{$ENDIF Include_Modules_Header}
(******************)
(* Implementation *)
(******************)
{$IFDEF Include_Modules_Implement}

 ResourceString
  _RTL_ResStr_ModularExeHalt = 'Program Termination';
  _RTL_ResStr_HaltInUnitProc = 'Halting unit initialzation / finalization';
  _RTL_ResStr_HaltInLibrary  = 'Library should not call halt';
  _RTL_ResStr_HaltExitCode   = 'Exit Code: $$$$$$$$';
  _RTL_ResStr_UnloadUnsafe   = 'Unsafe application unloading detected (there are concurrent RTL threads)';

 Var
  _INT_StartThreadTLS: TProc = NIL;
  _INT_EndThreadTLS: TProc = NIL;
  _INT_ExitProcessTLS: TProc = NIL;

 Procedure _INT_Halt(Const RetCode: Cardinal);
  BEGIN
   _WIN_ExitProcess(RetCode);
  END;

 Procedure _INT_Unload;
  Var ExeModule: PLibModule;
  BEGIN
   // If there are multiple threads running, we cannot safely unload
   // Note that the second case can happen if an alian thread (created by a non-Delphi library
   //  or injected into the process) calls Halt (directly or indirectly)
   IF (ThreadCount > 0) OR (_INT_MainThreadID <> _WIN_GetCurrentThreadID) then
    BEGIN
     _INT_LogResMsg(llWarn, @_RTL_ResStr_UnloadUnsafe);
     Exit;
    END;
   // ^^^ Reachable by any thread only in application context

   // Reachable when only main thread is running in application context
   ExeModule:= FindModule(_INT_MainInstance);
   IF Assigned(ExeModule) then
    Try
     ModuleFInit(ExeModule);
     UnregisterModule(ExeModule);

     _INT_ExitProcessTLS;
     _RTL_SafeShutdown:= True;
    Except
     _INT_LogExceptionResContext(@_RTL_ResStr_ModularExeHalt);
    END;
  END;

 Procedure _INT_ExceptionHalt;
  BEGIN
   // Reachable only by main thread from Delphi application
   // (when default exception handler is triggered)
   LogException;
   NotifyError;

   _INT_Unload;
   _INT_Halt(RTL_ErrorToExitCode(_INT_RTLError.Error));
  END;

 Procedure _Halt(Code: Cardinal);
  Procedure _LOC_LogHaltError(Const ErrType: PResStringRec);
   Var
    Context: Array [0..RTL_ResStr_MsgLen+2] of Char;
    MsgLen: Cardinal;
   BEGIN
    MsgLen:= LoadResStringFx(ErrType, Context, RTL_ResStr_MsgLen+1);
    Context[MsgLen]:= #13; Context[MsgLen+1]:= #10; Inc(MsgLen, 2);
    Inc(MsgLen, LoadResStringFx(@_RTL_ResStr_HaltExitCode, @Context[MsgLen], RTL_ResStr_MsgLen-MsgLen+1));
    RTL_CardinalToHex8(Code, @Context[MsgLen-8]);
    Log(llWarn, Context);
   END;
  BEGIN
   // Halt during unit initialization/finalization results in an initialzation failure error
   // Note that, for finalization it will only abort current unit's current finalization
   IF _INT_InitProgress > 0 then
    BEGIN
     _LOC_LogHaltError(@_RTL_ResStr_HaltInUnitProc);
     Error(reInitFailure);
    END;

   // Library should not use this method to terminate host process
   IF _INT_IsLibrary then
    BEGIN
     _LOC_LogHaltError(@_RTL_ResStr_HaltInLibrary);
     Error(reInvalidOp);
    END;
   // ^^^ Reachable by any thread in library or application context

   // Reachable by any thread only in application context
   ExitCode:= Code;
   _Halt0;
  END;

 Procedure _Halt0;
  BEGIN
   // Reachable only by main thread in application context
   // (when main block finished)
   _INT_Unload;
   _INT_Halt(ExitCode);
  END;

 Procedure _RTL_TLSProcs(Const TLSStartThread, TLSEndThread, TLSExitProcess: TProc);
  BEGIN
   _INT_StartThreadTLS:= TLSStartThread;
   _INT_EndThreadTLS:= TLSEndThread;
   _INT_ExitProcessTLS:= TLSExitProcess;
  END;

 Procedure _RTL_PrepareExe(Const LibModule: PLibModule);
  BEGIN
   // We are running an application
   _INT_IsLibrary:= False;
   _INT_MainInstance:= LibModule.Instance;
   _INT_MainThreadID:= _WIN_GetCurrentThreadID;

   _INT_SetDefaultExcHandler(@_INT_ExceptionHalt);
  END;

{$ENDIF Include_Modules_Implement}
(******************)
(* Initialization *)
(******************)
{$IFDEF Include_Modules_Initialize}
{$ENDIF Include_Modules_Initialize}
(****************)
(* Finalization *)
(****************)
{$IFDEF Include_Modules_Finalize}
{$ENDIF Include_Modules_Finalize}
